using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.ComponentModel.Composition.Primitives;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Windows.Threading;
using Caliburn.Micro;
using Paladyne.FileSearch.Core;
using Paladyne.FileSearch.UI.Properties;

namespace Paladyne.FileSearch.UI
{
    /// <summary>
    /// Application bootstrapper
    /// </summary>
    public class AppBootstrapper : Bootstrapper<IShell>
    {
        private CompositionContainer _container;

        /// <summary>
        /// By default, we are configured to use MEF
        /// </summary>
        protected override void Configure()
        {
            var catalog = new AggregateCatalog(
                AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()
                );

            var batch = new CompositionBatch();

            // Configure plugins folder
            if (Directory.Exists(App.ExtensionsPath))
            {
                var directoryCatalog = new DirectoryCatalog(App.ExtensionsPath);
                catalog.Catalogs.Add(directoryCatalog);
                App.RefreshExtensions(directoryCatalog);
                batch.AddExportedValue(directoryCatalog);
            }

            _container = new CompositionContainer(catalog);

            batch.AddExportedValue<IWindowManager>(new WindowManager());
            batch.AddExportedValue<IEventAggregator>(new EventAggregator());
            batch.AddExportedValue(_container);
            batch.AddExportedValue(catalog);

            // Register implementations
            // In this case Caliburn.Micro and MEF together act as a IoC-container implementing Service Locator pattern
            batch.AddExportedValue<ISearcher>(new FileSearcher());
            batch.AddExportedValue<ITextCompareStrategy>(new DefaultTextCompareStrategy());
            batch.AddExportedValue<IDisplayMessage>(new DefaultMessageShower());

            _container.Compose(batch);
        }

        /// <summary>
        /// Get registered type instance
        /// </summary>
        /// <param name="serviceType"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        protected override object GetInstance(Type serviceType, string key)
        {
            string contract = string.IsNullOrEmpty(key) ? AttributedModelServices.GetContractName(serviceType) : key;
            IEnumerable<object> exports = _container.GetExportedValues<object>(contract);

            if (exports.Count() > 0)
                return exports.First();

            throw new Exception(
                String.Format(Resources.AppBootstrapper_GetInstance_Could_not_locate_any_instances_of_contract__0__,
                              contract));
        }

        /// <summary>
        /// Get all registered type instances
        /// </summary>
        /// <param name="serviceType"></param>
        /// <returns></returns>
        protected override IEnumerable<object> GetAllInstances(Type serviceType)
        {
            return _container.GetExportedValues<object>(AttributedModelServices.GetContractName(serviceType));
        }

        /// <summary>
        /// Import types and register implementations
        /// </summary>
        /// <param name="instance"></param>
        protected override void BuildUp(object instance)
        {
            _container.SatisfyImportsOnce(instance);
        }

        /// <summary>
        /// Show error in case of unexpected exception
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void OnUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            string message = e.Exception is TargetInvocationException
                                 ? e.Exception.InnerException.Message
                                 : e.Exception.Message;
            e.Handled = true;
            IoC.Get<IDisplayMessage>().ShowError(message);
        }
    }
}